{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "04791372",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Shared secret matches.\n",
      "Alice's Private Key: 82606499507747440987050479812813078635285952037786416673976012283989331288142\n",
      "Alice's Public Key: (62514881804714428231192859056082221811533284700435585343955717198524964794335, 95177630059588049283095802652884455297801361561329699096982207819905288167168)\n",
      "Bob's Private Key: 88901982354510689156671390758696246479998240706232250638309895970936046480827\n",
      "Bob's Public Key: (12025260431957395985379890515856805874022361988002716563414115736497149099301, 79273637794819036250326671113309215648490064743739747012363645833579807535904)\n",
      "Shared Secret: (77451587716775443510009755960062594320580380408009722874722913678825763876550, 2649561184996041046878485472775414139434239070258036874344063326827993327163)\n"
     ]
    }
   ],
   "source": [
    "# Elliptic Curve Diffie-Hellman (ECDH) algorithm\n",
    "\n",
    "from py_ecc.secp256k1 import secp256k1\n",
    "import os\n",
    "\n",
    "def generate_keys():\n",
    "    private_key = int.from_bytes(os.urandom(32), 'big') % secp256k1.N\n",
    "    public_key = secp256k1.multiply(secp256k1.G, private_key)\n",
    "    return private_key, public_key\n",
    "\n",
    "# Alice and Bob generate their own key pairs\n",
    "alice_private, alice_public = generate_keys()\n",
    "bob_private, bob_public = generate_keys()\n",
    "\n",
    "# Calculate the shared secret\n",
    "shared_secret_alice = secp256k1.multiply(bob_public, alice_private)\n",
    "shared_secret_bob = secp256k1.multiply(alice_public, bob_private)\n",
    "\n",
    "if shared_secret_alice == shared_secret_bob:\n",
    "    print(\"Shared secret matches.\")\n",
    "else:        \n",
    "    print(\"Shared secret does not match!\")\n",
    "\n",
    "print(f\"Alice's Private Key: {alice_private}\")\n",
    "print(f\"Alice's Public Key: {alice_public}\")\n",
    "\n",
    "print(f\"Bob's Private Key: {bob_private}\")\n",
    "print(f\"Bob's Public Key: {bob_public}\")\n",
    "\n",
    "print(f\"Shared Secret: {shared_secret_alice}\")\n",
    "\n",
    "# Output:\n",
    "# Shared secret matches.\n",
    "# Alice's Private Key: 44226773042722162955098193291492534006186517732096623157459837212766793078584\n",
    "# Alice's Public Key: (113906392817926084413632896524344771269472367375880032535005632965062391078788, 49665636540644454541653315656482000530366349019751676160955522917215379042285)\n",
    "# Bob's Private Key: 51860882402071446551116109914681284224864199234652843480335793819475548437366\n",
    "# Bob's Public Key: (52340819409831460217804635786419806447405367609650964443132838196582132856471, 56429557458241459690871510882159471830396052430769816127197158365607969924309)\n",
    "# Shared Secret: (39817116182924354378808003014233470575110979407770339130416639641795260327693, 42970388080766198583159133018251494914868250846130428856587988974064644921855)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "a3a9123c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original Plaintext Message: (55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424)\n",
      "Encrypted Message: ((59830309720978449946889995874587870215667310393260257620370408789811812953791, 61955688499586988048604902054852801385524339809529140973272234470367112364645), (29105031096182708747750302737496064946401641390342920957387424407347571278406, 115327997712399318117788727312520711761194913441694122125064926039535571946108))\n",
      "Decrypted Message: (55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424)\n",
      "Message decrypted successfully!\n"
     ]
    }
   ],
   "source": [
    "from py_ecc.secp256k1 import secp256k1\n",
    "from random import randint\n",
    "\n",
    "def elgamal_encrypt(G, Y, M):\n",
    "    k = randint(1, secp256k1.N - 1)\n",
    "    C1 = secp256k1.multiply(G, k)\n",
    "    C2 = secp256k1.add(M, secp256k1.multiply(Y, k))\n",
    "    return (C1, C2)\n",
    "\n",
    "def elgamal_decrypt(C1, C2, x):\n",
    "    # Compute xC1 using the private key x\n",
    "    xC1 = secp256k1.multiply(C1, x)\n",
    "    # Compute M = C2 - xC1\n",
    "    M = secp256k1.add(C2, (xC1[0], -xC1[1]))\n",
    "    return M\n",
    "\n",
    "# Example parameters\n",
    "p = secp256k1.N\n",
    "G = secp256k1.G\n",
    "\n",
    "# Generate key pairs\n",
    "x, Y = generate_keys()\n",
    "\n",
    "# Assume the message M is a point on the curve, here we simply choose G as an example\n",
    "M = G\n",
    "print(\"Original Plaintext Message:\", M)\n",
    "\n",
    "# Encryption\n",
    "C1, C2 = elgamal_encrypt(G, Y, M)\n",
    "print(\"Encrypted Message:\", (C1, C2))\n",
    "\n",
    "# Decryption\n",
    "M_decrypted = elgamal_decrypt(C1, C2, x)\n",
    "print(\"Decrypted Message:\", M_decrypted)\n",
    "\n",
    "# Verification\n",
    "assert M == M_decrypted, \"Decryption failed!\"\n",
    "print(\"Message decrypted successfully!\")\n",
    "\n",
    "# Example output\n",
    "# Original Plaintext Message: (55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424)\n",
    "# Encrypted Message: ((87298472810248234319752437423707505477343664832890363292431829216099637291919, 39528614830056678009484946030376271359657183017625571564228160252781333158439), (67113196324182438503834247973075313606138491143388276462715763950508942145812, 59499979624168470896804403233074133393632477568643779021536973756576878140912))\n",
    "# Decrypted Message: (55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424)\n",
    "# Message decrypted successfully!\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "c841e756",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original Plaintext Message: b'Hello, ECDSA with secp256k1!'\n",
      "Signature: (93155020076571581891828391126177039141373114716842694147583265484823968588061, 23390424861672546041880313820955199567418091327609644416166133268827193110519)\n",
      "Signature Verification Result: True\n"
     ]
    }
   ],
   "source": [
    "# ECDSA\n",
    "\n",
    "from py_ecc.secp256k1 import secp256k1\n",
    "import os\n",
    "import hashlib\n",
    "\n",
    "def generate_keys():\n",
    "    # Generate private key\n",
    "    private_key = os.urandom(32)\n",
    "    private_key_int = int.from_bytes(private_key, 'big') % secp256k1.N\n",
    "    # Generate public key\n",
    "    public_key = secp256k1.multiply(secp256k1.G, private_key_int)\n",
    "    return private_key_int, public_key\n",
    "\n",
    "def ecdsa_sign(message, private_key):\n",
    "    # Hash the message\n",
    "    message_hash = hashlib.sha256(message).digest()\n",
    "    message_hash_int = int.from_bytes(message_hash, 'big')\n",
    "    \n",
    "    k = int.from_bytes(os.urandom(32), 'big') % secp256k1.N\n",
    "    R = secp256k1.multiply(secp256k1.G, k)\n",
    "    r = R[0] % secp256k1.N\n",
    "    s = ((message_hash_int + r * private_key) * secp256k1.inv(k, secp256k1.N)) % secp256k1.N\n",
    "    \n",
    "    return (r, s)\n",
    "\n",
    "def ecdsa_verify(message, signature, public_key):\n",
    "    r, s = signature\n",
    "    message_hash = hashlib.sha256(message).digest()\n",
    "    message_hash_int = int.from_bytes(message_hash, 'big')\n",
    "    \n",
    "    w = secp256k1.inv(s, secp256k1.N)\n",
    "    u1 = (message_hash_int * w) % secp256k1.N\n",
    "    u2 = (r * w) % secp256k1.N\n",
    "    \n",
    "    P = secp256k1.add(secp256k1.multiply(secp256k1.G, u1), secp256k1.multiply(public_key, u2))\n",
    "    \n",
    "    return r == P[0] % secp256k1.N\n",
    "\n",
    "# Example\n",
    "x, Y = generate_keys()\n",
    "M = b\"Hello, ECDSA with secp256k1!\"\n",
    "print(\"Original Plaintext Message:\", M)\n",
    "\n",
    "signature = ecdsa_sign(M, x)\n",
    "print(\"Signature:\", signature)\n",
    "\n",
    "valid = ecdsa_verify(M, signature, Y)\n",
    "print(\"Signature Verification Result:\", valid)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8f12eafb",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
